// -----------------------------
// Author: 		Dhaenens Gert	
// Creation: 	10/16/2014 6:53:05 PM				
// -----------------------------

#include "GeometryPass.h"

#include "Application.h"
#include "FileManager.h"

#include "RenderManager.h"
#include "Scene.h"
#include "Camera.h"
#include "MeshActor.h"
#include "Mesh.h"
#include "DefaultMaterial.h"

GeometryPass::GeometryPass(RenderManager* lpRenderManager, const RenderTargets& targets)
	: RenderPass(lpRenderManager, "Geometry", targets)
{
}

GeometryPass::~GeometryPass()
{
	
}

void GeometryPass::Init(Application* lpApp)
{
	
}
void GeometryPass::Apply(Application* lpApp, Scene* lpScene)
{
	IRenderDevice* lpDevice = m_lpRenderManager->GetDevice();

	// Bind Targets
	IRenderTarget* lpBindTargets[] = 
	{
		m_Targets.PositionTarget,
		m_Targets.NormalTarget,
		m_Targets.AlbedoTarget,
		m_Targets.AmbientTarget,
	};
	UInt32 uBindTargetCount = ARRAYSIZE(lpBindTargets);
	lpDevice->SetRenderTargets(lpBindTargets, uBindTargetCount, m_Targets.DepthStencil);

	// Get the Meshes to be Renderered
	vector<MeshActor*> meshActors = lpScene->GetActorsWithFlagsAs<MeshActor>(Actor::Geometry);

	// Get the Camera Properties
	Matrix view = lpScene->GetCamera()->GetView();
	Matrix projection = lpScene->GetCamera()->GetProjection();

	// Render the Meshes
	Mesh* lpLastMesh = nullptr;
	Material* lpLastMaterial = nullptr;
	for (MeshActor* lpActor : meshActors)
	{
		// Get the Mesh & Material
		Mesh* lpMesh = lpActor->GetMesh();
		Material* lpMaterial = lpActor->GetMaterial();

		// Make sure that we have
		// a mesh & material
		if (!lpMaterial || !lpMesh)
			continue;

		// Make sure the the Material is
		// a Deferred Type material, if not
		// Skip this actor since it will be rendererd
		// in the Forward Pass
		if (lpMaterial->GetRenderType() != Material::RenderType::Deferred)
			continue;

		// If it is a different mesh than before
		// Bind the vertex & index buffer
		if (lpMesh != lpLastMesh)
		{
			lpDevice->SetVertexBuffer(0, lpMesh->GetVertexBuffer());
			lpDevice->SetIndexBuffer(lpMesh->GetIndexBuffer());
		}

		// Update the Material Matrices
		lpMaterial->m_World = lpActor->GetWorld();
		lpMaterial->m_View = view;
		lpMaterial->m_Projection = projection;
		lpMaterial->UpdateMatrices(lpApp);

		// If it is a differen material than before
		// Update the material data
		// And bind itself
		if (lpMaterial != lpLastMaterial)
		{
			lpMaterial->UpdateData(lpApp);
			lpMaterial->Apply(lpApp);

			RenderState eState = m_lpRenderManager->GetRenderState();
			if (eState != RenderState::Wireframe)
			{
				if (eState == RenderState::Default && lpMaterial->IsTwoSided())
					m_lpRenderManager->SetRenderState(RenderState::DefaultNoCull, false);
				else if (eState == RenderState::DefaultNoCull && !lpMaterial->IsTwoSided())
					m_lpRenderManager->SetRenderState(RenderState::Default, false);
			}
		}

		// Draw the Mesh
		lpDevice->DrawIndexed(lpMesh->GetIndexBuffer()->GetCount(), 0, 0);

		// Save the Mesh & Material
		lpLastMesh = lpMesh;
		lpLastMaterial = lpMaterial;
	}

	// Unbind Targets
	IRenderTarget* lpUnbindTargets[] =
	{
		nullptr,
		nullptr,
		nullptr,
		nullptr,
	};
	UInt32 uUnbindTargetCount = ARRAYSIZE(lpUnbindTargets);
	lpDevice->SetRenderTargets(lpUnbindTargets, uUnbindTargetCount, nullptr);
}